using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using DevComponents.DotNetBar;
using System.IO;
using DevComponents.Editors;
using BodySoundSimulator.Properties;
using BodySoundSimulator.CaseData;
using BodySoundSimulator.CommonElements;
using System.Threading;
using BodySoundSimulator.AbsoluteMapData;
using BodySoundSimulator.Training.CallibrationWizard;
using BodySoundSimulator.Training.PerformanceTester;
using System.Runtime.InteropServices;

namespace BodySoundSimulator.Training
{
	public partial class TrainerDialog : Office2007RibbonForm
	{
		#region constants
		Pen p = new Pen(Color.Black);
		Pen stethLocationPen = new Pen(Color.Red);
		Pen originalStethLocationPen = new Pen(Color.Blue);
		const string videoFolderName = "Recorded";
		#endregion

		#region Fields
		Dictionary<string, CaseInformation> existingCases;
		AbsoluteBodyFeatures absoluteFeatures;
		CaseInformation loadedCaseInfo;

		TestingAuscultationSitePlayHelper auscultationSoundGeneratorHelper;
		Thread cameraCaptureThread;
		Thread fileCaptureThread;

		PointF previousStethLocation;
		PointF previousOriginalStethLocation;


		bool playingStarted;
		bool suspendUiEvents;

		bool showGrid = false;
		bool showSites = false;
		string videoFileName = string.Empty;
		#endregion

		#region Delegates
		delegate void DisplayPushedImageDelegate(byte[] imageData, int width, int height, PictureBox picBox);
		delegate void LocationDataUpdatedDelegate(StethLocationData locationData);
		delegate void CapturingFinishedDelegate();
		#endregion

		#region Properties
		public PatientView View
		{
			get
			{
				if (this.radioFrontView.Checked)
				{
					return PatientView.FrontView;
				}
				else
				{
					return PatientView.BackView;
				}
			}
		}

		public AuscultationConfiguration Configuration
		{
			get
			{
				if (this.cmbConfiguration.SelectedItem != null)
				{
					return ((AuscultationConfiguration)((BodySoundSimulator.CommonElements.ComboBoxItem)this.cmbConfiguration.SelectedItem).Tag);
				}
				return AuscultationConfiguration.NormalBreath;
			}
		}

		public StethLocationTrackingMode LocationTrackingMode
		{
			get
			{
				return GetStethoscopeTrackingMode();
			}
		}
		#endregion

		#region Constructors
		public TrainerDialog()
		{
			InitializeComponent();
			this.existingCases = SimulatorFileManager.GetExistingCaseInformation();
			this.absoluteFeatures = AbsoluteBodyFeaturesDataManipulator.LoadAbsoluteBodyFeatureData();
			InitializeUI();
		}
		#endregion

		#region Private methods
		private void InitializeUI()
		{
			EnableDisableControls(false);
			InitializeVideoListComboBox();
			LoadAuscultationConfigurationComboBoxes();
		}

		private void EnableDisableControls(bool val)
		{
			this.ribBarMode.Enabled = val;
			this.ribBarOperations.Enabled = val;
			this.ribBarView.Enabled = val;
			this.pnlDockContainerProperties.Enabled = val;
			this.pnlDisplay.Enabled = val;
		}

		private void InitializeVideoListComboBox()
		{
			this.suspendUiEvents = true;
			string path = Path.Combine(Application.StartupPath, videoFolderName);
			string[] videoFiles = Directory.GetFiles(path, "*.avi");
			this.cmbBoxVideoList.Items.Clear();
			foreach (string fileName in videoFiles)
			{
				ComboItem item = new ComboItem();
				item.Text = Path.GetFileName(fileName);
				item.Tag = fileName;
				item.Image = Resources.New_video;
				item.TextLineAlignment = StringAlignment.Center;
				this.cmbBoxVideoList.Items.Add(item);
			}
			if (this.cmbBoxVideoList.Items.Count > 0)
			{
				this.cmbBoxVideoList.SelectedIndex = 0;
				this.videoFileName = videoFiles[0];
			}
			this.suspendUiEvents = false;
		}

		private void LoadAuscultationConfigurationComboBoxes()
		{
			this.suspendUiEvents = true;
			this.cmbConfiguration.Items.Add(new BodySoundSimulator.CommonElements.ComboBoxItem("Normal Breath", AuscultationConfiguration.NormalBreath));
			this.cmbConfiguration.Items.Add(new BodySoundSimulator.CommonElements.ComboBoxItem("Deep Breath", AuscultationConfiguration.DeepBreath));
			this.cmbConfiguration.Items.Add(new BodySoundSimulator.CommonElements.ComboBoxItem("Held Breath", AuscultationConfiguration.HeldBreath));
			this.cmbConfiguration.SelectedIndex = 0;
			this.suspendUiEvents = false;
		}

		private void UpdateUI()
		{
			if (this.loadedCaseInfo != null)
			{
				this.txtCaseID.Text = this.loadedCaseInfo.CaseID;
				this.txtDiseaseName.Text = this.loadedCaseInfo.DiseaseName;
				this.txtSoundName.Text = this.loadedCaseInfo.SoundName;
				this.txtDetails.Text = this.loadedCaseInfo.PatientsClaim;
				this.picBoxBodyView.Invalidate();
			}
		}

		private StethLocationTrackingMode GetStethoscopeTrackingMode()
		{
			if (this.radioBtnIdleMode.Checked)
			{
				return StethLocationTrackingMode.Manual;
			}
			else if (this.radioBtnCameraMode.Checked)
			{
				return StethLocationTrackingMode.FromCamera;
			}
			else if (this.radioBtnVideoMode.Checked)
			{
				return StethLocationTrackingMode.FromVideo;
			}
			else
			{
				return StethLocationTrackingMode.None;
			}
		}

		private void InitializeStethoscopeTrackingMode()
		{
			switch (this.LocationTrackingMode)
			{
				case StethLocationTrackingMode.FromCamera:
					this.previousStethLocation = PointF.Empty;
					this.previousOriginalStethLocation = PointF.Empty;
					cameraCaptureThread = new Thread(new ThreadStart(StartCapturingFromCamera));
					cameraCaptureThread.Start();
					break;
				case StethLocationTrackingMode.FromVideo:
					this.previousStethLocation = PointF.Empty;
					this.previousOriginalStethLocation = PointF.Empty;
					fileCaptureThread = new Thread(new ThreadStart(StartCapturingFromFile));
					fileCaptureThread.Start();
					break;
				case StethLocationTrackingMode.Manual:
					//do nothing
					break;
				default:
					//do nothing
					break;
			}
		}

		private void CloseStethoscopeTrackingMode()
		{
			switch (this.LocationTrackingMode)
			{
				case StethLocationTrackingMode.FromCamera:
				case StethLocationTrackingMode.FromVideo:
					OpenCvAdapterNativeMethods.StopCapturing();
					break;
				case StethLocationTrackingMode.Manual:
					//do nothing
					break;
				default:
					//do nothing
					break;
			}
		}

		private void UpdateUiOnPlayStarting()
		{
			this.btnStart.Enabled = !this.playingStarted;
			this.btnStop.Enabled = this.playingStarted;

			this.radioBackView.Enabled = !this.playingStarted;
			this.radioFrontView.Enabled = !this.playingStarted;

			this.cmbConfiguration.Enabled = !this.playingStarted;

			this.ribBarMode.Enabled = !this.playingStarted;
			this.ribBarRecordedVideos.Enabled = !this.playingStarted;

			this.btnCallibrate.Enabled = !this.playingStarted;
			this.btnItmCallibration.Enabled = !this.playingStarted;

			this.btnOpen.Enabled = !this.playingStarted;
			this.btnItemOpen.Enabled = !this.playingStarted;

			this.btnPerformance.Enabled = !this.playingStarted;
			this.btnItmPerformance.Enabled = !this.playingStarted;
		}

		private void StartCapturingFromCamera()
		{
			OpenCvAdapterNativeMethods.StartCapturing(new OpenCvAdapterNativeMethods.ImagePushedDelegate(SourceImagePushed),
				new OpenCvAdapterNativeMethods.StethLocationDataPushedDelegate(LocationDataPushed));
		}

		private void StartCapturingFromFile()
		{
			if (!string.IsNullOrEmpty(this.videoFileName))
			{
				StringBuilder builder = new StringBuilder(this.videoFileName);
				OpenCvAdapterNativeMethods.StartCapturingFromFile(builder,
					new OpenCvAdapterNativeMethods.ImagePushedDelegate(SourceImagePushed),
					new OpenCvAdapterNativeMethods.StethLocationDataPushedDelegate(LocationDataPushed),
					new OpenCvAdapterNativeMethods.CapturingFinishedDelegate(CapturingFinished));
			}
		}

		private void SourceImagePushed(int width, int height, IntPtr imageDataPointer)
		{
			int size = height * width;
			byte[] imageData = new byte[size];
			Marshal.Copy(imageDataPointer, imageData, 0, size);
			if (this.picBoxVideo.InvokeRequired)
			{
				this.picBoxVideo.BeginInvoke(new DisplayPushedImageDelegate(DisplayPushedImage), imageData, width, height, this.picBoxVideo);
			}
			else
			{
				DisplayPushedImage(imageData, width, height, this.picBoxVideo);
			}
		}

		private void LocationDataPushed(IntPtr locationDataPtr)
		{
			StethLocationData locationData = new StethLocationData();
			Marshal.PtrToStructure(locationDataPtr, locationData);
			if (this.InvokeRequired)
			{
				this.BeginInvoke(new LocationDataUpdatedDelegate(UpdateAndDisplayLocationData), locationData);
			}
			else
			{
				UpdateAndDisplayLocationData(locationData);
			}
		}

		private void UpdateAndDisplayLocationData(StethLocationData locationData)
		{

			PointF currentStethLocation = StethLocationDataManipulator.GetMappedStethLocation(locationData);
			bool updateRequired = false;
			this.previousOriginalStethLocation = currentStethLocation;
			if (this.previousStethLocation == PointF.Empty)
			{
				this.previousStethLocation = currentStethLocation;
				updateRequired = true;
			}
			else
			{
				SoundMapCellInfo previousCellInfo = SoundMapDataManipulator.GetCellInfo(this.loadedCaseInfo.SoundMap,
					this.previousStethLocation,
					this.View);
				SoundMapCellInfo currentCellInfo = SoundMapDataManipulator.GetCellInfo(this.loadedCaseInfo.SoundMap,
					currentStethLocation,
					this.View);
				if (currentCellInfo != null
					&& previousCellInfo != null)
				{
					if (previousCellInfo.CellID != currentCellInfo.CellID)
					{
						this.previousStethLocation = currentStethLocation;
						updateRequired = true;
					}
				}
			}
			if (updateRequired)
			{
				this.auscultationSoundGeneratorHelper.StopPlaying();
				SoundMapCellInfo selecetedCellInfo = SoundMapDataManipulator.GetCellInfo(this.loadedCaseInfo.SoundMap,
						this.previousStethLocation,
						this.View);
				if (this.playingStarted)
				{
					this.auscultationSoundGeneratorHelper.StartPlaying(selecetedCellInfo);
					Thread.Sleep(100);

				}
			}

		}

		private void DisplayPushedImage(byte[] imageData, int width, int height, PictureBox picBox)
		{
			this.picBoxVideo.Image = BitmapCreationHelper.GetBitmapFromPixelData(imageData, width, height);
			this.picBoxVideo.Refresh();
			this.picBoxBodyView.Refresh();
		}

		private void CapturingFinished()
		{
			if (this.InvokeRequired)
			{
				this.BeginInvoke(new CapturingFinishedDelegate(OnCapturingFinished));
			}
			else
			{
				OnCapturingFinished();
			}
		}

		private void OnCapturingFinished()
		{
			this.auscultationSoundGeneratorHelper.CloseWaveOuts();
			this.playingStarted = false;
			UpdateUiOnPlayStarting();
		}
		#endregion

		#region UI event handlers

		private void btnOpen_Click(object sender, EventArgs e)
		{
			LoadCaseDialog caseLoadingDialog = new LoadCaseDialog(this.existingCases, false);
			if (caseLoadingDialog.ShowDialog() == DialogResult.OK)
			{
				this.suspendUiEvents = true;
				this.loadedCaseInfo = caseLoadingDialog.SelectedCaseInfo;
				EnableDisableControls(true);
				this.radioBackView.Checked = false;
				this.radioFrontView.Checked = true;
				this.cmbConfiguration.SelectedIndex = 0;
				this.auscultationSoundGeneratorHelper = new TestingAuscultationSitePlayHelper(this.loadedCaseInfo, this.Configuration, PatientView.FrontView);
				this.auscultationSoundGeneratorHelper.LoadSiteInfoList();
				UpdateUI();
				this.suspendUiEvents = false;
			}
		}

		private void btnCallibrate_Click(object sender, EventArgs e)
		{
			CallibrationWizardDialog wizard = new CallibrationWizardDialog();
			wizard.ShowDialog();
		}

		private void btnPerformance_Click(object sender, EventArgs e)
		{
			PerformanceTesterDlg performanceTesterDlg = new PerformanceTesterDlg();
			performanceTesterDlg.ShowDialog();
		}

		private void btnStart_Click(object sender, EventArgs e)
		{
			if (!AuscultationSiteInfoManipultor.InvalidCaseInfo(this.loadedCaseInfo, this.Configuration))
			{
				this.auscultationSoundGeneratorHelper.LoadWaveOuts();
				InitializeStethoscopeTrackingMode();
				this.playingStarted = true;
				UpdateUiOnPlayStarting();
			}
			else
			{
				MessageBox.Show("All sites must be assigned with a valid sound file.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void btnStop_Click(object sender, EventArgs e)
		{
			this.auscultationSoundGeneratorHelper.CloseWaveOuts();
			CloseStethoscopeTrackingMode();
			this.playingStarted = false;
			UpdateUiOnPlayStarting();
		}

		private void picBoxBodyView_Paint(object sender, PaintEventArgs e)
		{
			Graphics g = e.Graphics;
			g.TranslateTransform(AbsoluteBodyFeatures.OriginTranslateX, AbsoluteBodyFeatures.OriginTranslateY);
			if (loadedCaseInfo != null)
			{
				if (this.radioFrontView.Checked)
				{
					AbsoluteBodyFeaturesDataManipulator.DrawFrontView(g);
					if (this.showSites)
					{
						AuscultationSiteInfoManipultor.DrawFrontViewCaseInfo(g, this.loadedCaseInfo);
					}
					if (this.showGrid)
					{
						SoundMapDataManipulator.DrawFrontViewMap(g, this.loadedCaseInfo.SoundMap);
					}
				}
				else
				{
					AbsoluteBodyFeaturesDataManipulator.DrawBackView(g);
					if (this.showSites)
					{
						AuscultationSiteInfoManipultor.DrawBackViewCaseInfo(g, this.loadedCaseInfo);
					}
					if (this.showGrid)
					{
						SoundMapDataManipulator.DrawBackViewMap(g, this.loadedCaseInfo.SoundMap);
					}

				}
			}
			if (this.playingStarted
				&& this.LocationTrackingMode != StethLocationTrackingMode.Manual
				&& this.previousStethLocation != PointF.Empty)
			{
				int width = 5;
				if (this.previousStethLocation != PointF.Empty)
				{
					g.DrawRectangle(stethLocationPen, this.previousStethLocation.X - width / 2, this.previousStethLocation.Y + width / 2, width, width);
				}

				if (this.previousOriginalStethLocation != PointF.Empty)
				{
					g.DrawRectangle(originalStethLocationPen, this.previousOriginalStethLocation.X - width / 2, this.previousOriginalStethLocation.Y + width / 2, width, width);
				}
			}
		}

		private void picBoxBodyView_MouseDown(object sender, MouseEventArgs e)
		{
			if (this.LocationTrackingMode == StethLocationTrackingMode.Manual)
			{
				PointF measureingLocation = new PointF(e.Location.X - AbsoluteBodyFeatures.OriginTranslateX, e.Location.Y - AbsoluteBodyFeatures.OriginTranslateY);
				SoundMapCellInfo selecetedCellInfo = SoundMapDataManipulator.GetCellInfo(this.loadedCaseInfo.SoundMap,
						measureingLocation,
						this.View);
				if (this.playingStarted)
				{
					this.auscultationSoundGeneratorHelper.StartPlaying(selecetedCellInfo);
				}
			}
		}

		private void picBoxBodyView_MouseUp(object sender, MouseEventArgs e)
		{
			if (this.LocationTrackingMode == StethLocationTrackingMode.Manual)
			{
				this.auscultationSoundGeneratorHelper.StopPlaying();
			}
		}

		private void radioBtnMode_CheckedChanged(object sender, EventArgs e)
		{
			this.ribBarRecordedVideos.Visible = this.radioBtnVideoMode.Checked;
		}

		private void cmbBoxVideoList_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (!suspendUiEvents)
			{
				if (this.cmbBoxVideoList.SelectedItem != null)
				{
					this.videoFileName = ((ComboItem)this.cmbBoxVideoList.SelectedItem).Tag.ToString();
				}
			}
		}

		private void btnShowGrid_CheckedChanged(object sender, EventArgs e)
		{
			if (!suspendUiEvents)
			{
				this.showGrid = this.btnShowGrid.Checked;
				this.showSites = this.btnShowSites.Checked;
				this.picBoxBodyView.Invalidate();
			}
		}
		
		private void TrainerDialog_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (this.auscultationSoundGeneratorHelper != null)
			{
				this.auscultationSoundGeneratorHelper.CloseWaveOuts();
			}
			CloseStethoscopeTrackingMode();
		}
		#endregion
	}
}